package com.jhe.hexed;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;

/**
 * Created by IntelliJ IDEA.
 * User: laullon
 * Date: 09-abr-2003
 * Time: 12:47:32
 */
@SuppressWarnings("serial")
public class JHexEditorHEX extends JComponent implements MouseListener, KeyListener, MouseMotionListener
{
	private JHexEditor he;
    private int cursor=0;
	private JPopupMenu popup = new JPopupMenu();
	
	public JHexEditorHEX(JHexEditor he)
    {
        this.he=he;
        addMouseListener(this);
        addMouseMotionListener(this);
        addKeyListener(this);
        addFocusListener(he);
        initPopUp();
    }

    private void initPopUp()
    {
		JMenuItem itemSelectAll = new JMenuItem("Select all");
	    itemSelectAll.addActionListener(new SelectAllCommand());
	    popup.add(itemSelectAll);
    	popup.addSeparator();
    	if (he.hasClipboard())
    	{
			JMenuItem itemCopy = new JMenuItem("Copy", new ImageIcon("images/copy_icon.png"));
		    itemCopy.addActionListener(new CopyCommand());
		    popup.add(itemCopy);
			JMenuItem itemPaste = new JMenuItem("Paste", new ImageIcon("images/paste_icon.png"));
		    itemPaste.addActionListener(new PasteCommand());
		    popup.add(itemPaste);
	    	popup.addSeparator();
    	}
		JMenuItem itemClear = new JMenuItem("Set to 0");
	    itemClear.addActionListener(new ClearCommand());
	    popup.add(itemClear);
		JMenuItem itemFill = new JMenuItem("Fill...");
	    itemFill.addActionListener(new FillCommand());
	    popup.add(itemFill);
    	popup.addSeparator();
		JMenuItem itemSave = new JMenuItem("Save...", new ImageIcon("images/file_save.png"));
	    itemSave.addActionListener(new SaveCommand());
	    popup.add(itemSave);
		JMenuItem itemLoad = new JMenuItem("Load...", new ImageIcon("images/file_load.png"));
	    itemLoad.addActionListener(new LoadCommand());
	    popup.add(itemLoad);
	}

	public Dimension getPreferredSize()
    {
        debug("getPreferredSize()");
        return getMinimumSize();
    }

    public Dimension getMaximumSize()
    {
        debug("getMaximumSize()");
        return getMinimumSize();
    }

    public Dimension getMinimumSize()
    {
        debug("getMinimumSize()");

        Dimension d=new Dimension();
        FontMetrics fn=getFontMetrics(JHexEditor.font);
        int h=fn.getHeight();
        int nl=he.getLineas();
        d.setSize(((fn.stringWidth(" ")+1)*+((16*3)-1))+(he.border*2)+1,h*nl+(he.border*2)+1);
        return d;
    }

    public void paint(Graphics g)
    {
        debug("paint("+g+")");
        debug("cursor="+he.selectionData.cursor+" buffer length="+he.data.getBufferLength());
        Dimension d=getMinimumSize();
        g.setColor(Color.white);
        g.fillRect(0,0,d.width,d.height);
        g.setColor(Color.black);

        g.setFont(JHexEditor.font);

        int ini=he.getInicio()*16;
        int fin=ini+(he.getLineas()*16);
        if(fin>he.data.getBufferLength()) fin=he.data.getBufferLength();

        //datos hex
        int x=0;
        int y=0;
    	for(int n=ini;n<fin;n++)
        {
			if (n >= he.selectionData.getSelectionFirst() && n <= he.selectionData.getSelectionLast())
        	{
        		if (he.selectionData.isInSelection())
        		{
	                g.setColor(Color.yellow);
	                he.fondo(g,(x*3),y,2);
	                g.setColor(Color.black);
        		}
        		else if (n==he.selectionData.cursor)
        		{
        			drawCursor(g, x ,y);
        		}
        		else
        		{
	                g.setColor(Color.blue);
	                he.fondo(g,(x*3),y,2);
	                g.setColor(Color.white);
        		}
        	}
        	else if (n==he.selectionData.cursor)
            {
                drawCursor(g, x, y);
            }
        	else
            {
                g.setColor(Color.black);
            }

            String s=("0"+Integer.toHexString(he.data.getBufferByte(n)));
            s=s.substring(s.length()-2);
            he.printString(g,s,((x++)*3),y);
            if(x==16)
            {
                x=0;
                y++;
            }
        }
    }

	private void drawCursor(Graphics g, int x, int y) {
		if(isFocusOwner())
		{
		    g.setColor(Color.black);
		    he.fondo(g,(x*3),y,2);
		    g.setColor(Color.blue);
		    he.fondo(g,(x*3)+cursor,y,1);
		} else
		{
		    g.setColor(Color.blue);
		    he.cuadro(g,(x*3),y,2);
		}

		if(isFocusOwner()) g.setColor(Color.white); else g.setColor(Color.black);
	}

    private void debug(String s)
    {
        he.debugPrintLine("JHexEditorHEX ==> "+s);
    }

    // calcular la posicion del raton
    public int calcularPosicionRaton(int x,int y)
    {
        FontMetrics fn=getFontMetrics(JHexEditor.font);
        x=x/((fn.stringWidth(" ")+1)*3);
        y=y/fn.getHeight();
        debug("x="+x+" ,y="+y);
        return x+((y+he.getInicio())*16);
    }

    // mouselistener
	@Override
    public void mouseClicked(MouseEvent event)
    {
        debug("mouseClicked("+event+")");
		if (!checkPopup(event))
			handleSelect(event.getPoint());
    }

	@Override
    public void mousePressed(MouseEvent event)
    {
        debug("mousePressed("+event+")");
    	if (!checkPopup(event) && event.getButton() == MouseEvent.BUTTON1)
    		beginDrag(event.getPoint());
    }

	@Override
    public void mouseReleased(MouseEvent event)
    {
        debug("mouseReleased("+event+")");
    	if (!checkPopup(event) && he.selectionData.isInSelection())
    		endDrag(event.getPoint());
    }

	@Override
    public void mouseEntered(MouseEvent event)
    {
        debug("mouseEntered("+event+")");
    }

	@Override
    public void mouseExited(MouseEvent event)
    {
        debug("mouseExited("+event+")");
        if (he.selectionData.isInSelection())
        	cancelDrag();
    }

	// mousemotionlistener
	@Override
	public void mouseDragged(MouseEvent event)
	{
        debug("mouseDragged("+event+")");
        if (!checkPopup(event) && he.selectionData.isInSelection())
        {
        	if (isInside(event.getPoint()))
        		doDrag(event.getPoint());
        	else
        		cancelDrag();
        }
	}

	private boolean isInside(Point relativePoint)
	{
		Point absolutePoint = new Point(relativePoint);
		absolutePoint.move((int)getBounds().getX(), (int)getBounds().getY());
		return getBounds().contains(absolutePoint);
	}

	@Override
	public void mouseMoved(MouseEvent event)
	{
        debug("mouseMoved("+event+")");
	}

    private boolean checkPopup(MouseEvent event)
    {
    	if (event.isPopupTrigger())
    	{
    		handleSelect(event.getPoint());
			popup.show(this, event.getX(), event.getY());
			return true;
    	}
    	return false;
    }
    
    private void handleSelect(Point point)
    {
        he.selectionData.cursor=calcularPosicionRaton((int)point.getX(), (int)point.getY());
        updateView();
    }

	private void updateView() {
		this.requestFocus();
        he.repaint();
	}

	private void beginDrag(Point point)
	{
        he.selectionData.startSelect(calcularPosicionRaton((int)point.getX(), (int)point.getY()));
        updateView();
	}

	private void doDrag(Point point)
	{
        he.selectionData.updateSelection(calcularPosicionRaton((int)point.getX(), (int)point.getY()));
        updateView();
	}

	private void endDrag(Point point)
	{
    	he.selectionData.endSelect(calcularPosicionRaton((int)point.getX(), (int)point.getY()));
        updateView();
	}
	
	private void cancelDrag()
	{
		he.selectionData.clear();
		updateView();
	}
    
	//KeyListener
	@Override
    public void keyTyped(KeyEvent e)
    {
        debug("keyTyped("+e+")");

        char c=e.getKeyChar();
        if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))||((c>='a')&&(c<='f')))
        {
            char[] str=new char[2];
            String n="00"+Integer.toHexString((int)he.data.getBufferByte(he.selectionData.cursor));
            if(n.length()>2) n=n.substring(n.length()-2);
            str[1-cursor]=n.charAt(1-cursor);
            str[cursor]=e.getKeyChar();
            he.data.setBufferByte(he.selectionData.cursor, (byte)Integer.parseInt(new String(str),16));

            if(cursor!=1) cursor=1;
            else if(he.selectionData.cursor!=(he.data.getBufferLength()-1)){ he.selectionData.cursor++; cursor=0;}
            he.actualizaCursor();
        }
    }

	@Override
    public void keyPressed(KeyEvent e)
    {
        debug("keyPressed("+e+")");
        he.keyPressed(e);
    }

	@Override
    public void keyReleased(KeyEvent e)
    {
        debug("keyReleased("+e+")");
        he.keyReleased(e);
    }

    public boolean isFocusTraversable()
    {
        return true;
    }

	public Frame getFrame()
	{
		Container container = he.getParent();
		while (container != null)
		{
			if (container instanceof Frame)
				return (Frame)container;
			container = container.getParent();
		}
		return null;
	}
    
    public class FillCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			String fillPattern = JOptionPane.showInputDialog(he, "Enter fill pattern (sequence of hex numbers)");
			if (fillPattern != null && !fillPattern.isEmpty())
			{
				String[] patternElements = fillPattern.split(" ");
				byte[] numbers = new byte[patternElements.length];
				int i = 0;
				try 
				{
					for (i = 0; i < patternElements.length; i++)
					{
						numbers[i] = (byte)Integer.parseInt(new String(patternElements[i]),16);
					}
					i = 0;
					for (int j = he.selectionData.getSelectionStart(); j <= he.selectionData.getSelectionEnd(); j++)
					{
						he.data.setBufferByte(j, numbers[i]);
						i = (i+1)%numbers.length;
					}
				}
				catch (NumberFormatException ex)
				{
					JOptionPane.showMessageDialog(he, patternElements[i] + " is not a valid hex number");
				}
			}
		}
	}

    public class ClearCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			for (int i = he.selectionData.getSelectionStart(); i <= he.selectionData.getSelectionEnd(); i++)
				he.data.setBufferByte(i, (byte)0);
			updateView();
		}
	}

    public class CopyCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			String data = "";
			for (int i = he.selectionData.getSelectionStart(); i <= he.selectionData.getSelectionEnd(); i++)
				data += (char)(he.data.getBufferByte(i));
			he.setClipboardContents(data);

		}
	}
    
    public class PasteCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			String data = he.getClipboardContents();
			if (data != null && !data.isEmpty())
			{
				int end_pos = he.selectionData.cursor;
				for (int i = 0; i < data.length() && end_pos < he.data.getBufferLength(); i++, end_pos++)
				{
					he.data.setBufferByte(end_pos, (byte)data.charAt(i));
				}
				if (--end_pos > he.selectionData.cursor)
				{
					he.selectionData.selectRange(he.selectionData.cursor, end_pos);
				}
			}
			else
			{
				he.selectionData.clear();
			}
			updateView();
		}
	}

    public class SelectAllCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			he.selectionData.selectRange(0, he.data.getBufferLength() - 1);
			updateView();
		}
	}

    public class SaveCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			FileDialog fd = null;
			fd = new FileDialog(getFrame(), "Save data to file", FileDialog.SAVE);
			fd.setVisible(true);
			if (fd.getDirectory() != null && fd.getFile() != null)
			{
				String selected_file = fd.getDirectory() + fd.getFile();
				if (!selected_file.isEmpty())
				{
					he.saveData(selected_file, he.selectionData.getSelectionStart(), he.selectionData.getSelectionEnd());
				}
			}
		}
	}

    public class LoadCommand implements ActionListener
    {
		@Override
		public void actionPerformed(ActionEvent e)
		{
			FileDialog fd = null;
			fd = new FileDialog(getFrame(), "Load data from file", FileDialog.LOAD);
			fd.setVisible(true);
			if (fd.getDirectory() != null && fd.getFile() != null)
			{
				String selected_file = fd.getDirectory() + fd.getFile();
				if (!selected_file.isEmpty())
				{
					he.loadData(selected_file, he.selectionData.cursor);
				}
			}
		}
	}
}
